﻿using Microsoft.Extensions.Options;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;

namespace BDRUtils.Cached
{
    /// <summary>
    /// redis基础类
    /// </summary>
    public class RedisHelperBase
    {
        /// <summary>
        /// 获取通用缓存时间
        /// </summary>
        /// <returns></returns>
        private TimeSpan GetCommonTimeSpan() => TimeSpan.FromDays(1);

        /// <summary>
        /// redis连接复用器
        /// </summary>
        private readonly ConnectionMultiplexer Connetction;

        /// <summary>
        /// 数据库
        /// </summary>
        private readonly IDatabase Db;

        /// <summary>
        /// RedisKey 的前缀
        /// </summary>
        private readonly string PrexKey;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="options"></param>
        public RedisHelperBase(IOptions<RedisSettingOption> options)
        {
            var setting = options.Value;
            if (setting != null)
            {
                PrexKey = setting.RedisKeyPrex ?? "frapro";
                var config = new ConfigurationOptions
                {
                    AbortOnConnectFail = false,
                    AllowAdmin = true,
                    ConnectTimeout = 15000,
                    SyncTimeout = 15000,
                    Ssl = false,
                    Password = setting.RedisPassword,//Redis数据库密码
                    EndPoints = { setting.RedisServer }// connectionString 为IP:Port 如”192.168.2.110:6379”
                };
                Connetction = ConnectionMultiplexer.Connect(config);
                Db = Connetction.GetDatabase(setting.RedisDb);
            }
        }

        /// <summary>
        /// 获取带前缀的rediskey(暂时不做配置)
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        private static string AddKeyPrefix(string key)
        {
            //return $"{DefaultKey}:{key}";
            return $"{key}";
        }

        /// <summary>
        /// 获取服务节点
        /// </summary>
        /// <returns></returns>
        public EndPoint[] GetEndPoints() => Connetction.GetEndPoints();

        /// <summary>
        /// 增加/修改
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool SetValue(string key, string value) => Db.StringSet(key, value, expiry: GetCommonTimeSpan());

        /// <summary>
        /// 增加/修改
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="timeSpan"></param>
        /// <returns></returns>
        public bool SetValue(string key, string value, TimeSpan timeSpan) => Db.StringSet(key, value, expiry: timeSpan);

        /// <summary>
        ///  增加/修改
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="timeSpan"></param>
        /// <returns></returns>
        public async Task<bool> SetValueAsync(string key, string value, TimeSpan timeSpan)
        {
            RedisKey redisKey = AddKeyPrefix(key);
            return await Db.StringSetAsync(redisKey, value, expiry: timeSpan);
        }

        /// <summary>
        /// 根据key获取缓存对象
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<string> GetValueAsync(string key)
        {
            RedisKey redisKey = AddKeyPrefix(key);
            return await Db.StringGetAsync(redisKey);
        }

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string GetValue(string key)
        {
            return Db.StringGet(key);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool DeleteKey(string key)
        {
            return Db.KeyDelete(key);
        }

        /// <summary>
        /// 返回所有匹配前缀的key
        /// </summary>
        /// <param name="endPoint">Network address</param>
        /// <param name="prefix">String key pattern</param>
        /// <returns>List of cache keys</returns>
        public IEnumerable<RedisKey> GetKeys(EndPoint endPoint, string prefix = null)
        {
            var server = Connetction.GetServer(endPoint);

            var keys = server.Keys(Db.Database, string.IsNullOrEmpty(prefix) ? null : $"{prefix}*");

            return keys;
        }

        /// <summary>
        /// 获取指定key
        /// </summary>
        /// <param name="prefix"></param>
        /// <returns></returns>
        public IEnumerable<RedisKey> GetKeys(string prefix = null)
        {
            var endPoints = Connetction.GetEndPoints();

            var server = Connetction.GetServer(endPoints[0]);

            var keys = server.Keys(Db.Database, string.IsNullOrEmpty(prefix) ? null : $"{prefix}*");

            return keys;
        }

        /// <summary>
        /// 移出所有匹配Key前缀的项
        /// </summary>
        /// <param name="prefix">String key prefix</param>
        public void RemoveByPrefix(string prefix)
        {

            foreach (var endPoint in Connetction.GetEndPoints())
            {
                var keys = GetKeys(endPoint, prefix);

                Db.KeyDelete(keys.ToArray());
            }
        }

        /// <summary>
        /// 清理所有的缓存数据
        /// </summary>
        public virtual void Clear()
        {
            foreach (var endPoint in Connetction.GetEndPoints())
            {
                var keys = GetKeys(endPoint).ToArray();

                Db.KeyDelete(keys);
            }
        }

        /// <summary>
        /// 将一个值插入到列表头部
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public virtual long LPush(string key, string value) => Db.ListLeftPush(key, value);

        /// <summary>
        /// 将一个值插入到列表头部
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public virtual async Task<long> LPushAsync(string key, string value) => await Db.ListLeftPushAsync(key, value);

        /// <summary>
        /// 将一个值插入到列表尾部
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public virtual long RPush(string key, string value) => Db.ListRightPush(key, value);

        /// <summary>
        /// 将一个值指插入到列表尾部
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public virtual async Task<long> RPushAsync(string key, string value) => await Db.ListRightPushAsync(key, value);
    }
}
